/**
 *******************************************************************************
 * @file      ee_driver.c
 * @version   V1.0.0    
 * @date      2013.05.20
 * @brief     EE driver for Tananis.	
 *            Resources: 
 *            None, call I2C_transfer function
 *
 *******************************************************************************
 * @author    - Adela 
 *            - Robert Zhang <armner@gmail.com>
 *            - 
 *
 */
#include "stm32f2xx.h"
#include "ee_driver.h"
#include "interrupt.h"
#include "FreeRTOS.h"
#include "semphr.h"
#include "i2c_driver.h"
#include "stdlib.h"
#include "string.h"
#include "led_driver.h"

#define I2C_FLASH_PAGESIZE  (64)

void EE_BufferRead(char* pBuffer, uint16_t ReadAddr, uint16_t NumByteToRead);
void EE_BufferWrite(char* pBuffer, uint16_t WriteAddr, uint16_t NumByteToWrite);
xSemaphoreHandle ee_sema=NULL;

int ee_open(Driver *driver)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    //disable WP
    GPIOB->BSRRH =GPIO_Pin_9;

    ee_sema =xSemaphoreCreateMutex();
    return 0;
}

int ee_write(Driver *driver, void *buffer, int len, OFFSET offset)
{
    //offset as ee address
    xSemaphoreTake(ee_sema, portMAX_DELAY);
    EE_BufferWrite(buffer, (uint16_t)offset, len);
    xSemaphoreGive(ee_sema);
    return len;
}

int ee_read(Driver *driver, void *buffer, int len, OFFSET offset)
{
    //offset as ee address
    xSemaphoreTake(ee_sema, portMAX_DELAY);
    EE_BufferRead(buffer, (uint16_t)offset, len);
    xSemaphoreGive(ee_sema);
    return len;
}

int ee_io_control(Driver *driver, int io_type, int data)
{
    return 0;
}

int ee_close(Driver *driver)
{
    vSemaphoreDelete(ee_sema);
    return 0;
}

void EE_PageWrite(char* pBuffer, uint16_t WriteAddr, uint8_t NumByteToWrite)
{
    char t_char[128+2];
    
    I2C_transfer_block i2c_block[1];
    
    memcpy(t_char+2, pBuffer, NumByteToWrite);
    t_char[0] =WriteAddr >>8;
    t_char[1] =WriteAddr;
    
    i2c_block[0].addr =0xA2;
    i2c_block[0].rw =I2C_WRITE;
    i2c_block[0].stop_mode =I2C_WRITE_APPEND_STOP;
    i2c_block[0].ack_mode =I2C_READ_RESPOND_ACK; //any
    i2c_block[0].len =NumByteToWrite+2;
    i2c_block[0].buf =t_char;
    
    i2c_transfer(i2c_block,1);
}

void EE_WaitEepromStandbyState(void)
{
    char t_char[2];
    I2C_transfer_block i2c_block[1];

    i2c_block[0].addr =0xA2;
    i2c_block[0].rw =I2C_WRITE;
    i2c_block[0].stop_mode =I2C_WRITE_BYPASS_STOP;
    i2c_block[0].ack_mode =I2C_READ_RESPOND_ACK; //any
    i2c_block[0].len =0;
    i2c_block[0].buf =t_char;

    while(1)
    {
        if(0==i2c_transfer(i2c_block,1))
        {
            break;
        }
    }
    
    i2c_block[0].stop_mode =I2C_WRITE_APPEND_STOP;
    i2c_transfer(i2c_block,1);

}

void EE_BufferRead(char* pBuffer, uint16_t ReadAddr, uint16_t NumByteToRead)
{
    char t_char[2];
    I2C_transfer_block i2c_block[2];
    
    t_char[0] =ReadAddr>>8;
    t_char[1] =ReadAddr;
    i2c_block[0].addr =0xA2;
    i2c_block[0].rw =I2C_WRITE;
    i2c_block[0].stop_mode =I2C_WRITE_BYPASS_STOP;
    i2c_block[0].ack_mode =I2C_READ_RESPOND_ACK; //any
    i2c_block[0].len =2;
    i2c_block[0].buf =t_char;

    i2c_block[1].addr =0xA2;
    i2c_block[1].rw =I2C_READ;
    i2c_block[1].stop_mode =I2C_WRITE_BYPASS_STOP;
    i2c_block[1].ack_mode =I2C_READ_RESPOND_NACK; //any
    i2c_block[1].len =NumByteToRead;
    i2c_block[1].buf =pBuffer;
    
    i2c_transfer(i2c_block,2);
}

/**
  * @brief  Writes buffer of data to the I2C EEPROM.
  * @param  pBuffer : pointer to the buffer  containing the data to be
  *   written to the EEPROM.
  * @param  WriteAddr : EEPROM's internal address to write to.
  * @param  NumByteToWrite : number of bytes to write to the EEPROM.
  * @retval None
  */
void EE_BufferWrite(char* pBuffer, uint16_t WriteAddr, uint16_t NumByteToWrite)
{
  uint8_t NumOfPage = 0, NumOfSingle = 0, count = 0;
  uint16_t Addr = 0;

  Addr = WriteAddr % I2C_FLASH_PAGESIZE;
  count = I2C_FLASH_PAGESIZE - Addr;
  NumOfPage =  NumByteToWrite / I2C_FLASH_PAGESIZE;
  NumOfSingle = NumByteToWrite % I2C_FLASH_PAGESIZE;

  /* If WriteAddr is I2C_FLASH_PAGESIZE aligned  */
  if (Addr == 0) {
    /* If NumByteToWrite < I2C_FLASH_PAGESIZE */
    if (NumOfPage == 0) {
      EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);

      EE_WaitEepromStandbyState();
    }
    /* If NumByteToWrite > I2C_FLASH_PAGESIZE */
    else {
      while (NumOfPage--) {
        EE_PageWrite(pBuffer, WriteAddr, I2C_FLASH_PAGESIZE);
        EE_WaitEepromStandbyState();
        WriteAddr += I2C_FLASH_PAGESIZE;
        pBuffer += I2C_FLASH_PAGESIZE;
      }

      if (NumOfSingle != 0) {
        EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);
        EE_WaitEepromStandbyState();
      }
    }
  }
  /* If WriteAddr is not I2C_FLASH_PAGESIZE aligned  */
  else {
    /* If NumByteToWrite < I2C_FLASH_PAGESIZE */
    if (NumOfPage== 0) {
      /* If the number of data to be written is more than the remaining space
      in the current page: */
      if (NumByteToWrite > count) {
        /* Write the data conained in same page */
        EE_PageWrite(pBuffer, WriteAddr, count);
        EE_WaitEepromStandbyState();

        /* Write the remaining data in the following page */
        EE_PageWrite((char*)(pBuffer + count), (WriteAddr + count), (NumByteToWrite - count));
        EE_WaitEepromStandbyState();
      }
      else {
        EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);
        EE_WaitEepromStandbyState();
      }
    }
    /* If NumByteToWrite > I2C_FLASH_PAGESIZE */
    else {
      NumByteToWrite -= count;
      NumOfPage =  NumByteToWrite / I2C_FLASH_PAGESIZE;
      NumOfSingle = NumByteToWrite % I2C_FLASH_PAGESIZE;

      if (count != 0) {
        EE_PageWrite(pBuffer, WriteAddr, count);
        EE_WaitEepromStandbyState();
        WriteAddr += count;
        pBuffer += count;
      }

      while (NumOfPage--) {
        EE_PageWrite(pBuffer, WriteAddr, I2C_FLASH_PAGESIZE);
        EE_WaitEepromStandbyState();
        WriteAddr +=  I2C_FLASH_PAGESIZE;
        pBuffer += I2C_FLASH_PAGESIZE;
      }
      if (NumOfSingle != 0) {
        EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);
        EE_WaitEepromStandbyState();
      }
    }
  }
}

Driver ee_driver =
{
    &ee_open,
    &ee_write,
    &ee_read,
    &ee_io_control,
    &ee_close
};
